#include "lyj_filenode.h"
#include "sys/mman.h"
#include <sddl.h>
#include <io.h>
#include <fcntl.h>


void lyj_filenode_initw(lyj_filenode_t* fn, LPCWSTR file_name)
{
    lyj_type_memzero(fn);
    fn->file_name_len = wcslen(file_name);
    lyj_memcpy(fn->file_name, file_name, (fn->file_name_len + 1) * sizeof(WCHAR));
    fn->parent_path_len = fn->file_name_len;
    while (fn->parent_path_len > 0)
    {
        if (fn->file_name[fn->parent_path_len - 1] == '\\' || fn->file_name[fn->parent_path_len - 1] == '/') break;
        fn->parent_path_len--;
    }
}

void lyj_filenode_destroy(lyj_filenode_t* fn)
{
    if (!fn) return;
    if (fn->handle) CloseHandle(fn->handle);
    if (fn->security.buf) free(fn->security.buf);
    if (fn->head_buffer.buf) free(fn->head_buffer.buf);
    //if (fn->mmap_buffer.buf)
    //{

    //    lyj_wlog("munmap %p %zu", fn->mmap_buffer.buf, fn->mmap_buffer.len);
    //    munmap(fn->mmap_buffer.buf, fn->mmap_buffer.len);
    //}
    lyj_free(fn);
}

void lyj_meta_get(lyj_kv_store_t* kv, lyj_meta_t* meta, lyj_int64_t file_index)
{

    lyj_char_t key_buf[128]; 
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0); 
    key.len = _snprintf(key.buf, 128, "META:%llx", file_index);
    lyj_kv_buf_t value = lyj_kv_buf_null;
    if (LYJ_RET_OK != lyj_kv_store_get(kv, &key, &value) || value.len != sizeof(lyj_meta_t))
    {
        lyj_type_memzero(meta);
    }
    else
    {
        lyj_memcpy(meta, value.buf, value.len);

    }
    if (value.buf)free(value.buf);
}

lyj_ret_t lyj_meta_put(lyj_kv_store_t* kv, lyj_meta_t* meta, lyj_int64_t file_index)
{
    lyj_char_t key_buf[128];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    key.len = _snprintf(key.buf, 128, "META:%llx", file_index);
    lyj_kv_buf_t value = lyj_kv_buf(meta, sizeof(lyj_meta_t));
    return lyj_kv_store_put(kv, &key, &value);
}

lyj_inline static lyj_ret_t lyj_meta_delete(lyj_kv_store_t* kv, lyj_int64_t file_index)
{
    lyj_char_t key_buf[128];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    key.len = _snprintf(key.buf, 128, "META:%llx", file_index);
    return lyj_kv_store_delete(kv, &key);
}
lyj_inline static lyj_ret_t lyj_child_put(lyj_kv_store_t* kv, lyj_int64_t parent_file_index, lyj_int64_t file_index, LPCWSTR child_file_name, lyj_size_t child_file_name_len)
{
    if (file_index == 0) return LYJ_RET_ERROR;
    WCHAR key_buf[1024];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    key.len = _swprintf(key_buf, L"CHILD:%llx:", parent_file_index);
    lyj_memcpy(key_buf + key.len, child_file_name, child_file_name_len * sizeof(WCHAR));
    key.len += child_file_name_len;
    key.len *= sizeof(WCHAR);
    lyj_kv_buf_t value = lyj_kv_buf(&file_index, sizeof(lyj_int64_t));
    return lyj_kv_store_put(kv, &key, &value);
}

lyj_inline static lyj_ret_t lyj_child_delete(lyj_kv_store_t* kv, lyj_int64_t parent_file_index, lyj_int64_t file_index, LPCWSTR child_file_name, lyj_size_t child_file_name_len)
{
    if (file_index == 0) return LYJ_RET_ERROR;
    WCHAR key_buf[1024];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    key.len = _swprintf(key_buf, L"CHILD:%llx:", parent_file_index);
    lyj_memcpy(key_buf + key.len, child_file_name, child_file_name_len * sizeof(WCHAR));
    key.len += child_file_name_len;
    key.len *= sizeof(WCHAR);
    return lyj_kv_store_delete(kv, &key);
}

lyj_inline static lyj_int64_t lyj_child_get_file_index(lyj_kv_store_t* kv, lyj_int64_t file_index, LPCWSTR child_file_name, lyj_size_t child_file_name_len)
{
    lyj_int64_t ret = -1;
    WCHAR key_buf[1024]; 
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0); 
    key.len = _swprintf(key_buf, L"CHILD:%llx:", file_index);
    lyj_memcpy(key_buf + key.len, child_file_name, child_file_name_len * sizeof(WCHAR));
    key.len += child_file_name_len;
    key.len *= sizeof(WCHAR);
    lyj_kv_buf_t value = lyj_kv_buf_null;
    lyj_kv_store_get(kv, &key, &value);
    if (value.buf && value.len == sizeof(lyj_int64_t))
    {
        ret = *(lyj_int64_t*)value.buf;
    }
    if (value.buf) free(value.buf);
    return ret;
}

lyj_inline static lyj_int64_t lyj_get_file_index(lyj_kv_store_t* kv, LPCWSTR file_name, lyj_size_t file_name_len, lyj_int64_t *parent_index)
{
    if (parent_index) *parent_index = -1;
    if (file_name[0] != '/' && file_name[0] != '\\') return -1;
    if (file_name_len == 1) return 0;
    lyj_size_t start = 1;
    lyj_size_t end = 1;
    lyj_int64_t file_index = 0;
    if (parent_index) *parent_index = file_index;

    while (end <= file_name_len)
    {
        if (file_name[end] == '/' || file_name[end] == '\\' || end == file_name_len)
        {
            if (parent_index) *parent_index = file_index;
            if (file_index >= 0)
            {
                file_index = lyj_child_get_file_index(kv, file_index, file_name + start, end - start);
                start = end + 1;
                end = start;
            }
            continue;
        }
        end += 1;
    }
    return file_index;
}

lyj_inline static lyj_int64_t lyj_gen_file_index(lyj_kv_store_t* kv)
{
    const static lyj_kv_buf_t key = lyj_kv_buf("FILE_INDEX", 10);
    lyj_kv_buf_t value = lyj_kv_buf_null;
    lyj_kv_buf_t new_value = lyj_kv_buf_null;
    lyj_int64_t ret;
    lyj_kv_store_lock(kv);
    if (LYJ_RET_OK != lyj_kv_store_get(kv, &key, &value) || value.len != sizeof(lyj_int64_t))
    {
        ret = 1;
    }
    else
    {
        ret = (*(lyj_int64_t*)value.buf);
    }
    ret++;
    new_value.buf = &ret;
    new_value.len = sizeof(lyj_int64_t);
    lyj_kv_store_put(kv, &key, &new_value);
    lyj_kv_store_unlock(kv);
    if (value.buf) free(value.buf);
    return ret - 1;
}

lyj_ret_t lyj_filenode_init_root(lyj_kv_store_t* kv)
{
    WCHAR buffer[1024];
    WCHAR final_buffer[2048];
    PTOKEN_USER user_token = NULL;
    PTOKEN_GROUPS groups_token = NULL;
    HANDLE token_handle;
    LPTSTR user_sid_str = NULL;
    LPTSTR group_sid_str = NULL;
    lyj_filenode_t fn;

    // Build default root filenode SecurityDescriptor
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token_handle) == FALSE)
    {
        lyj_log("Failed init root resources");
        return LYJ_RET_ERROR;
    }
    DWORD return_length;
    if (!GetTokenInformation(token_handle, TokenUser, buffer, sizeof(buffer), &return_length))
    {
        CloseHandle(token_handle);
        lyj_log("Failed init root resources");
        return LYJ_RET_ERROR;
    }
    user_token = (PTOKEN_USER)buffer;
    if (!ConvertSidToStringSid(user_token->User.Sid, &user_sid_str)) {
        CloseHandle(token_handle);
        lyj_log("Failed init root resources");
        return LYJ_RET_ERROR;
    }
    if (!GetTokenInformation(token_handle, TokenGroups, buffer, sizeof(buffer), &return_length)) {
        CloseHandle(token_handle);
        lyj_log("Failed init root resources");
        return LYJ_RET_ERROR;
    }
    groups_token = (PTOKEN_GROUPS)buffer;
    if (groups_token->GroupCount > 0) {
        if (!ConvertSidToStringSid(groups_token->Groups[0].Sid, &group_sid_str)) {
            CloseHandle(token_handle);
            lyj_log("Failed init root resources");
            return LYJ_RET_ERROR;
        }
        swprintf_s(buffer, 1024, L"O:%lsG:%ls", user_sid_str, group_sid_str);
    }
    else {
        swprintf_s(buffer, 1024, L"O:%ls", user_sid_str);
    }
    LocalFree(user_sid_str);
    LocalFree(group_sid_str);
    CloseHandle(token_handle);
    swprintf_s(final_buffer, 2048, L"%lsD:PAI(A;OICI;FA;;;AU)", buffer);
    PSECURITY_DESCRIPTOR security_descriptor = NULL;
    ULONG size = 0;
    if (!ConvertStringSecurityDescriptorToSecurityDescriptor(final_buffer, SDDL_REVISION_1, &security_descriptor, &size))
    {
        lyj_log("Failed init root resources");
        return LYJ_RET_ERROR;
    }
    lyj_type_memzero(&fn);
    fn.meta.is_directory = LYJ_TRUE;
    fn.meta.is_exist = LYJ_TRUE;
    fn.meta.attributes = FILE_ATTRIBUTE_DIRECTORY;
    lyj_meta_put(kv, &fn.meta, 0);
    lyj_filenode_security_set(kv, &fn, security_descriptor, size);
    LocalFree(security_descriptor);
    return LYJ_RET_OK;
}

void lyj_filenode_get(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{
    lyj_int64_t file_index = lyj_get_file_index(kv, fn->file_name, fn->file_name_len, &fn->parent_index);
    if (file_index < 0) 
    {
        fn->meta.is_exist = LYJ_FALSE;
        return;
    }
    lyj_meta_get(kv, &fn->meta, file_index);
}
NTSTATUS lyj_filenode_add(lyj_kv_store_t* kv, lyj_filenode_t* fn, const PDOKAN_IO_SECURITY_CONTEXT security_context)
{
    if(fn->parent_index < 0)  return STATUS_OBJECT_PATH_NOT_FOUND;
    lyj_meta_get(kv, &fn->parent_meta, fn->parent_index);
    if (!fn->parent_meta.is_exist || !fn->parent_meta.is_directory)
    {
        lyj_wlog(L"Add: No directory exist FilePath: %ls(%llu)",  fn->file_name, fn->parent_path_len);
        return STATUS_OBJECT_PATH_NOT_FOUND;
    }
    if (fn->meta.is_exist)
    {
        fn->meta.file_size = 0;
    }
    else
    {
        fn->meta.is_exist = LYJ_TRUE;
        fn->meta.parent_file_index = fn->parent_index;
        fn->meta.file_index = lyj_gen_file_index(kv);
    }
    lyj_filetimes_reset(&fn->meta.times);
    lyj_filenode_set(kv, fn);
    if (security_context && security_context->AccessState.SecurityDescriptor)
    {
        lyj_wlog(L"Attach SecurityDescriptor %ls", fn->file_name);
        lyj_filenode_security_set(kv, fn, security_context->AccessState.SecurityDescriptor, GetSecurityDescriptorLength(security_context->AccessState.SecurityDescriptor));
    }
    lyj_wlog(L"Add %ls: %ls index:%llu", fn->meta.is_directory ? L"dir" : L"file", fn->file_name, fn->meta.file_index);
    return STATUS_SUCCESS;
}
NTSTATUS lyj_filenode_set(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{
    lyj_ret_t ret;
    ret = lyj_meta_put(kv, &fn->meta, fn->meta.file_index);
    if (ret != LYJ_RET_OK) return STATUS_DEVICE_NOT_READY;
    ret = lyj_child_put(kv, fn->meta.parent_file_index, fn->meta.file_index, fn->file_name + fn->parent_path_len, fn->file_name_len - fn->parent_path_len);
    if (ret != LYJ_RET_OK) return STATUS_DEVICE_NOT_READY;
    return STATUS_SUCCESS;
}

NTSTATUS lyj_filenode_move(lyj_kv_store_t* kv, lyj_filenode_t* old_fn, lyj_filenode_t* new_fn)
{
    if (new_fn->parent_index < 0) return STATUS_OBJECT_PATH_NOT_FOUND;
    lyj_meta_get(kv, &new_fn->parent_meta, new_fn->parent_index);
    if (!new_fn->parent_meta.is_exist || !new_fn->parent_meta.is_directory)return STATUS_OBJECT_PATH_NOT_FOUND;
    if (new_fn->meta.is_exist) lyj_filenode_delete(kv, new_fn);
    lyj_child_delete(kv, old_fn->meta.parent_file_index, old_fn->meta.file_index, old_fn->file_name + old_fn->parent_path_len, old_fn->file_name_len - old_fn->parent_path_len);
    old_fn->meta.parent_file_index = new_fn->parent_index;
    lyj_size_t skip = lyj_filenode_skip_size;
    lyj_memcpy(((char*)old_fn) + skip, ((char*)new_fn) + skip, sizeof(lyj_filenode_t) - skip);
    lyj_child_put(kv, old_fn->meta.parent_file_index, old_fn->meta.file_index, old_fn->file_name + old_fn->parent_path_len, old_fn->file_name_len - old_fn->parent_path_len);
    return STATUS_SUCCESS;
}
typedef struct lyj_filenode_children_arg_s {
    void* arg;
    lyj_filenode_children_callback_f callback;
    lyj_kv_buf_t prefix;
    lyj_kv_store_t* kv;
    lyj_meta_t meta;
}lyj_filenode_children_arg_t;
lyj_inline static void lyj_filenode_children_callback(void* _arg, const lyj_kv_buf_t* key, const lyj_kv_buf_t* value)
{
    lyj_filenode_children_arg_t* arg = (lyj_filenode_children_arg_t*)_arg;
    if (value->len == sizeof(lyj_int64_t))
    {
        lyj_meta_get(arg->kv, &arg->meta, *(lyj_int64_t*)value->buf);
        if (arg->meta.file_index)
        {
            arg->callback(arg->arg, (WCHAR *)(((char *)key->buf) + arg->prefix.len), (key->len - arg->prefix.len) / sizeof(WCHAR), &arg->meta);
        } 
    }
}
void lyj_filenode_children(lyj_kv_store_t* kv, lyj_filenode_t* fn, void* _arg, lyj_filenode_children_callback_f callback)
{
    lyj_filenode_children_arg_t arg;
    lyj_type_memzero(&arg);
    arg.arg = _arg;
    arg.callback = callback;
    arg.kv = kv;

    WCHAR key_buf[1024];
    arg.prefix.buf = key_buf;
    arg.prefix.len = sizeof(WCHAR) * _swprintf(key_buf, L"CHILD:%llx:", fn->meta.file_index);
    lyj_kv_store_range_prefix(kv, &arg.prefix, &arg, lyj_filenode_children_callback);

}
lyj_bool_t lyj_filenode_children_exist(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{
    WCHAR key_buf[1024];
    lyj_kv_buf_t prefix = { key_buf, 0 };
    prefix.len = sizeof(WCHAR) * _swprintf(key_buf, L"CHILD:%llx:", fn->meta.file_index);
    return lyj_kv_store_range_prefix_exist(kv, &prefix);
}

void lyj_filenode_security_get(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{
    lyj_char_t key_buf[128];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    key.len = _snprintf(key.buf, 128, "SECURIT:%llx", fn->meta.file_index);
    if (fn->security.buf)
    {
        free(fn->security.buf);
        fn->security.buf = NULL;
    }
    lyj_kv_store_get(kv, &key, &fn->security);
}
void lyj_filenode_security_set(lyj_kv_store_t* kv, lyj_filenode_t* fn, void* security_buf, lyj_size_t security_len)
{
    lyj_char_t key_buf[128];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    lyj_kv_buf_t value = lyj_kv_buf(security_buf, security_len);
    key.len = _snprintf(key.buf, 128, "SECURIT:%llx", fn->meta.file_index);

    if (fn->security.buf) free(fn->security.buf);
    fn->security.len = 0;
    if (!value.buf || value.len == 0)
    {
        lyj_kv_store_delete(kv, &key);
    }
    else
    {
        lyj_kv_store_put(kv, &key, &value);
    }
}

typedef struct lyj_mmap_data_s {
    char* mmap_buffer;
    lyj_size_t mmap_size;
    void* data_buffer;
}lyj_mmap_data_t;
lyj_inline static void lyj_buffer_mmap_by_fd(int fd, lyj_size_t offset, lyj_size_t size, lyj_mmap_data_t* mmap_data, lyj_bool_t write)
{
    lyj_size_t alignment_offset = offset & 0xffffffffffff0000LLU;
    lyj_size_t add_size = offset - alignment_offset;
    lyj_type_memzero(mmap_data);
    mmap_data->mmap_size = size + add_size;
    //mmap_data->mmap_size |= 0xffff;
    //mmap_data->mmap_size++;

    mmap_data->mmap_buffer = (char*)mmap(NULL, mmap_data->mmap_size, PROT_READ | (write ? PROT_WRITE : 0), MAP_SHARED, fd, alignment_offset);
    if (mmap_data->mmap_buffer == MAP_FAILED) mmap_data->mmap_buffer = NULL;
    if (!mmap_data->mmap_buffer)
    {
        lyj_real_log("mmap error %d", errno);
        mmap_data->data_buffer = NULL;
    }
    else
    {
        mmap_data->data_buffer = mmap_data->mmap_buffer + add_size;
    }
}
lyj_inline static void lyj_buffer_mmap(const char* file_name, lyj_size_t offset, lyj_size_t size, lyj_mmap_data_t* mmap_data, lyj_bool_t write)
{
    HANDLE file = CreateFileA(file_name,
        FILE_GENERIC_EXECUTE | FILE_GENERIC_READ | FILE_GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    mmap_data->data_buffer = NULL;
    if (file == INVALID_HANDLE_VALUE)
    {
        lyj_real_log("open %s error %d", file_name, GetLastError());
        return;
    }
    int fd = _open_osfhandle((intptr_t)file, O_BINARY | O_CREAT | (write ? O_RDWR : O_RDONLY));
    // int fd = _open(file_name, O_DIRECT|O_BINARY | O_CREAT | (write ? O_RDWR : O_RDONLY), 00777);
    if (fd < 0)
    {
        lyj_real_log("open %s error %d", file_name, errno);
        return;
    }
    lyj_buffer_mmap_by_fd(fd, offset, size, mmap_data, write);
    _close(fd);
}
lyj_inline static void lyj_buffer_munmap(lyj_mmap_data_t* mmap_data)
{
    if (mmap_data->mmap_buffer) munmap(mmap_data->mmap_buffer, mmap_data->mmap_size);
}
#if LYJ_HEADER_BUFFER_KV
void lyj_filenode_head_buffer_get(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{
    lyj_char_t key_buf[128];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    lyj_kv_buf_t value = lyj_kv_buf_null;
    if (!fn->head_buffer.buf)
    {
        fn->head_buffer.buf = lyj_malloc(LYJ_HEAD_BUFFER_SIZE);
    }
    if (fn->meta.file_size == 0)
    {
        fn->head_buffer.len = 0;
        return;
    }
    key.len = _snprintf(key.buf, 128, "HEAD:%llx", fn->meta.file_index);
    lyj_kv_store_get(kv, &key, &value);
    if (value.buf)
    {
        lyj_memcpy(fn->head_buffer.buf, value.buf, value.len);
        free(value.buf);
    }
    fn->head_buffer.len = value.len;

}
void lyj_filenode_head_buffer_set(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{
    lyj_char_t key_buf[128];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    key.len = _snprintf(key.buf, 128, "HEAD:%llx", fn->meta.file_index);
    lyj_kv_store_put(kv, &key, &fn->head_buffer);
}

lyj_inline static void lyj_filenode_head_buffer_delete(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{
    lyj_char_t key_buf[128];
    lyj_kv_buf_t key = lyj_kv_buf(key_buf, 0);
    key.len = _snprintf(key.buf, 128, "HEAD:%llx", fn->meta.file_index);
    lyj_kv_store_delete(kv, &key);
}
#else
lyj_inline static void lyj_filenode_head_buffer_mmap(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_size_t offset, lyj_size_t size, lyj_mmap_data_t *mmap_data, lyj_bool_t write)
{
    lyj_buffer_mmap_by_fd(lyj_kv_store_get_head_file_fd(kv), offset, size, mmap_data, write);
}
lyj_size_t lyj_filenode_head_buffer_get(lyj_kv_store_t* kv, lyj_filenode_t* fn, void* buffer, lyj_size_t size, lyj_size_t offset)
{
    lyj_size_t max_size = lyj_min(fn->meta.file_size, LYJ_HEAD_BUFFER_SIZE);
    if (offset >= max_size)
    {
        lyj_log("error offset %zu > %zu", offset, max_size);
        return 0;
    }
    if (offset + size > max_size)
    {
        lyj_log("error size %zu offset %zu, change size to %zu", size, offset, max_size - offset);
        size = max_size - offset;
    }
    if (size == 0)return 0;
    offset += fn->meta.file_index * LYJ_HEAD_BUFFER_SIZE;
#if LYJ_HEADER_BUFFER_MMAP
    lyj_mmap_data_t mmap_data;
    lyj_filenode_head_buffer_mmap(kv, fn, offset, size, &mmap_data, LYJ_FALSE);
    if (!mmap_data.data_buffer) return 0;
    lyj_memcpy(buffer, mmap_data.data_buffer, size);
    lyj_buffer_munmap(&mmap_data);
#else
    OVERLAPPED overlap;
    DWORD read_size;
    lyj_type_memzero(&overlap);
    overlap.Offset = offset & 0xFFFFFFFF;
    overlap.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
    if (!ReadFile(lyj_kv_store_get_head_file_handle(kv), buffer, (DWORD)size, &read_size, &overlap))
    {
        lyj_real_log("read error %d", GetLastError());
    }
    size = read_size;
#endif
    return size;

}
lyj_size_t lyj_filenode_head_buffer_set(lyj_kv_store_t* kv, lyj_filenode_t* fn, const void* buffer, lyj_size_t size, lyj_size_t offset)
{
    if (offset >= LYJ_HEAD_BUFFER_SIZE)
    {
        lyj_log("error offset %zu > %d", offset, LYJ_HEAD_BUFFER_SIZE);
        return 0;
    }
    if (offset + size > LYJ_HEAD_BUFFER_SIZE)
    {
        lyj_log("error size %zu offset %zu, change size to %zu", size, offset, LYJ_HEAD_BUFFER_SIZE - offset);
        size = LYJ_HEAD_BUFFER_SIZE - offset;
    }
    if (size == 0) return 0;
    offset += fn->meta.file_index * LYJ_HEAD_BUFFER_SIZE;
#if LYJ_HEADER_BUFFER_MMAP
    lyj_mmap_data_t mmap_data;
    lyj_filenode_head_buffer_mmap(kv, fn, offset, size, &mmap_data, LYJ_TRUE);
    if (!mmap_data.data_buffer) return 0;

    if (buffer)
    {
        lyj_memcpy(mmap_data.data_buffer, buffer, size);
    }
    else
    {
        lyj_memzero(mmap_data.data_buffer, size);
    }
    lyj_buffer_munmap(&mmap_data);
#else
    OVERLAPPED overlap;
    DWORD write_size;
    lyj_type_memzero(&overlap);
    overlap.Offset = offset & 0xFFFFFFFF;
    overlap.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
    if (!WriteFile(lyj_kv_store_get_head_file_handle(kv), buffer, (DWORD)size, &write_size, &overlap))
    {
        lyj_real_log("write error %d", GetLastError());
    }
    size = write_size;
#endif
    return size;
}

lyj_inline static void lyj_filenode_head_buffer_delete(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{

}
#endif
#if LYJ_LARGE_FILE_MMAP
lyj_inline static void lyj_filenode_buffer_mmap(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_size_t offset, lyj_size_t size, lyj_mmap_data_t *mmap_data, lyj_bool_t write)
{
    char file_name[1024];
    _snprintf(file_name, 1024, "%s\\%llx", lyj_kv_store_get_file_path(kv), fn->meta.file_index);
    lyj_buffer_mmap(file_name, offset, size, mmap_data, write);
}
lyj_size_t lyj_filenode_buffer_get(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_char_t* buffer, lyj_size_t size, lyj_size_t offset)
{
    lyj_mmap_data_t mmap_data;
    lyj_wlog(L"%ls offset:%zu size:%zu", fn->file_name, offset, size);
    if (offset < LYJ_HEAD_BUFFER_SIZE || size == 0) return 0;
    offset -= LYJ_HEAD_BUFFER_SIZE;
    lyj_filenode_buffer_mmap(kv, fn, offset, size, &mmap_data, LYJ_FALSE);
    if (!mmap_data.data_buffer) return 0;
    lyj_memcpy(buffer, mmap_data.data_buffer, size);
    lyj_buffer_munmap(&mmap_data);
    return size;
}
lyj_size_t lyj_filenode_buffer_set(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_char_t* buffer, lyj_size_t size, lyj_size_t offset)
{
    lyj_mmap_data_t mmap_data;
    lyj_wlog(L"%ls offset:%zu size:%zu", fn->file_name, offset, size);
    if (offset < LYJ_HEAD_BUFFER_SIZE || size == 0) return 0;
    offset -= LYJ_HEAD_BUFFER_SIZE;
    lyj_filenode_buffer_mmap(kv, fn, offset, size, &mmap_data, LYJ_TRUE);
    if (!mmap_data.data_buffer) return 0;
    lyj_memcpy(mmap_data.data_buffer, buffer, size);
    lyj_buffer_munmap(&mmap_data);
    return size;
}
void lyj_filenode_buffer_set_size(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_size_t size)
{
    lyj_mmap_data_t mmap_data;
    if (size < fn->meta.file_size || size <= LYJ_HEAD_BUFFER_SIZE) return;

    lyj_filenode_buffer_mmap(kv, fn, fn->meta.file_size, size - fn->meta.file_size, &mmap_data, LYJ_TRUE);
    if (!mmap_data.data_buffer) return;
    lyj_memzero(mmap_data.data_buffer, size);
    lyj_buffer_munmap(&mmap_data);
}
#else

lyj_inline static HANDLE lyj_filenode_buffer_openfile(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_bool_t write)
{
    if (fn->handle) return fn->handle;
    char file_name[1024];
    _snprintf(file_name, 1024, "%s\\%llx", lyj_kv_store_get_file_path(kv), fn->meta.file_index);
    
    HANDLE file = CreateFileA(file_name,
        fn->generic_desired_access,
        fn->shareaccess,
        NULL,
        OPEN_ALWAYS,
        fn->meta.attributes,
        NULL);
    //HANDLE file = CreateFileA(file_name,
    //    FILE_GENERIC_READ | FILE_GENERIC_WRITE | (write ? FILE_GENERIC_WRITE : 0),
    //    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    //    NULL,
    //    OPEN_ALWAYS,
    //    0,
    //    NULL);
    if (file == INVALID_HANDLE_VALUE)
    {
        lyj_real_log("open %s error %d", file_name, GetLastError());
        return NULL;
    }
    fn->handle = file;
    return file;
}
lyj_size_t lyj_filenode_buffer_get(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_char_t* buffer, lyj_size_t size, lyj_size_t offset)
{
    DWORD read_size = 0;
    lyj_wlog(L"%ls offset:%zu size:%zu", fn->file_name, offset, size);
    if (offset < LYJ_HEAD_BUFFER_SIZE || size == 0) return 0;
    offset -= LYJ_HEAD_BUFFER_SIZE;
    HANDLE file = lyj_filenode_buffer_openfile(kv, fn, LYJ_FALSE);
    if (!file) return 0;
    OVERLAPPED overlap;
    lyj_type_memzero(&overlap);
    overlap.Offset = offset & 0xFFFFFFFF;
    overlap.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
    if (!ReadFile(file, buffer, (DWORD)size, &read_size, &overlap))
    {
        lyj_real_log("read error %d", GetLastError());
    }
    //CloseHandle(file);
    return read_size;
}
lyj_size_t lyj_filenode_buffer_set(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_char_t* buffer, lyj_size_t size, lyj_size_t offset)
{
    DWORD write_size = 0;
    lyj_wlog(L"%ls offset:%zu size:%zu", fn->file_name, offset, size);
    if (offset < LYJ_HEAD_BUFFER_SIZE || size == 0) return 0;
    offset -= LYJ_HEAD_BUFFER_SIZE;
    HANDLE file = lyj_filenode_buffer_openfile(kv, fn, LYJ_TRUE);
    if (!file) return 0;
    OVERLAPPED overlap;
    lyj_type_memzero(&overlap);
    overlap.Offset = offset & 0xFFFFFFFF;
    overlap.OffsetHigh = (offset >> 32) & 0xFFFFFFFF;
    if (!WriteFile(file, buffer, (DWORD)size, &write_size, &overlap))
    {
        lyj_real_log("write error %d", GetLastError());
    }
    //CloseHandle(file);
    return write_size;
}
void lyj_filenode_buffer_set_size(lyj_kv_store_t* kv, lyj_filenode_t* fn, lyj_size_t size)
{
    if (size < fn->meta.file_size || size <= LYJ_HEAD_BUFFER_SIZE) return;
    size -= LYJ_HEAD_BUFFER_SIZE;
    HANDLE file = lyj_filenode_buffer_openfile(kv, fn, LYJ_TRUE);
    if (!file) return;

    LARGE_INTEGER li_start, li_size;
    li_start.QuadPart = 0;
    li_size.QuadPart = size;
    if (SetFilePointerEx(file, li_start, NULL, FILE_CURRENT) == ~0 ||
        SetFilePointerEx(file, li_size, NULL, FILE_BEGIN) == ~0 ||
        !SetEndOfFile(file)) {

        lyj_real_log("set_size error %d", GetLastError());
    }
    //CloseHandle(file);
}
#endif
void lyj_filenode_delete(lyj_kv_store_t* kv, lyj_filenode_t* fn)
{
    char file_name[1024];
    lyj_meta_delete(kv, fn->meta.file_index);
    lyj_child_delete(kv, fn->meta.parent_file_index, fn->meta.file_index, fn->file_name + fn->parent_path_len, fn->file_name_len - fn->parent_path_len);
    lyj_filenode_head_buffer_delete(kv, fn);
    if (fn->meta.file_size > LYJ_HEAD_BUFFER_SIZE)
    {
        _snprintf(file_name, 1024, "%s\\%llx", lyj_kv_store_get_file_path(kv), fn->meta.file_index);
        DeleteFileA(file_name);
    }
}